package br.schindler.games.android.gl.base;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.CharBuffer;
import java.nio.FloatBuffer;
import java.util.ArrayList;

import javax.microedition.khronos.opengles.GL10;

import android.content.Context;
import android.content.res.AssetManager;
import android.util.Log;

public class GLModel {

	/**
	 * Creates a new model
	 * 
	 * @param context
	 * @param name
	 * @throws IOException
	 */
	public GLModel(Context context, String name) throws IOException {
		mContext = context;
		loadObj(name);
		Log.e("Model", "LOADED");
	}

	/**
	 * Indicates texture ID
	 * 
	 * @param id_text
	 */
	public void InitTex() {
		textures = new GLTextures(gl, mContext);
	}

	/**
	 * Initiate a GL
	 * 
	 * @param gl
	 * @param id_text
	 */
	public void Init(GL10 gl) {
		this.gl = gl;
		InitTex();
	}
	
	/**
	 * Adcionar uma textura
	 * @param id
	 */
	public void addTexture(int id){
		textures.add(id);
	}

	/**
	 * Loads all textures
	 * @see addTexture
	 */
	public void loadTextures(){
		textures.loadTextures();
	}
	
	/**
	 * Loads the model
	 * 
	 * @param name
	 * @throws IOException
	 */
	private void loadObj(String name) throws IOException {

		AssetManager am = mContext.getAssets();
		String str;
		String[] tmp;
		String[] ftmp;
		float v;
		ArrayList<Float> vlist = new ArrayList<Float>();
		ArrayList<Float> tlist = new ArrayList<Float>();
		ArrayList<Float> nlist = new ArrayList<Float>();
		ArrayList<Fp> fplist = new ArrayList<Fp>();

		BufferedReader inb = new BufferedReader(new InputStreamReader(am.open(name)), 1024);

		while ((str = inb.readLine()) != null) {
			tmp = str.split(" ");
			
			if (tmp[0].equalsIgnoreCase("v")) {

				for (int i = 1; i < 4; i++) {
					try{
					  v = Float.parseFloat(tmp[i]);
					  vlist.add(v);
					}catch (Exception e) {
						e.printStackTrace();
						System.exit(0);
						break;
					}
					
				}

			}
			if (tmp[0].equalsIgnoreCase("vn")) {

				for (int i = 1; i < 4; i++) {
					v = Float.parseFloat(tmp[i]);
					nlist.add(v);
				}

			}
			if (tmp[0].equalsIgnoreCase("vt")) {
				for (int i = 1; i < 3; i++) {
					v = Float.parseFloat(tmp[i]);
					tlist.add(v);
				}

			}
			if (tmp[0].equalsIgnoreCase("f")) {
				for (int i = 1; i < 4; i++) {
					ftmp = tmp[i].split("/");

					long chi = Integer.parseInt(ftmp[0]) - 1;
					int cht = Integer.parseInt(ftmp[1]) - 1;
					int chn = Integer.parseInt(ftmp[2]) - 1;

					fplist.add(new Fp(chi, cht, chn));
				}
			}
		}

		ByteBuffer vbb = ByteBuffer.allocateDirect(fplist.size() * 4 * 3);
		vbb.order(ByteOrder.nativeOrder());
		mVertexBuffer = vbb.asFloatBuffer();

		ByteBuffer vtbb = ByteBuffer.allocateDirect(fplist.size() * 4 * 2);
		vtbb.order(ByteOrder.nativeOrder());
		mTexBuffer = vtbb.asFloatBuffer();

		ByteBuffer nbb = ByteBuffer.allocateDirect(fplist.size() * 4 * 3);
		nbb.order(ByteOrder.nativeOrder());
		mNormBuffer = nbb.asFloatBuffer();

		for (int j = 0; j < fplist.size(); j++) {
			mVertexBuffer.put(vlist.get((int) (fplist.get(j).Vi * 3)));
			mVertexBuffer.put(vlist.get((int) (fplist.get(j).Vi * 3 + 1)));
			mVertexBuffer.put(vlist.get((int) (fplist.get(j).Vi * 3 + 2)));

			mTexBuffer.put(-tlist.get(fplist.get(j).Ti * 2));
			mTexBuffer.put((tlist.get((fplist.get(j).Ti * 2) + 1)));

			mNormBuffer.put(nlist.get(fplist.get(j).Ni * 3));
			mNormBuffer.put(nlist.get((fplist.get(j).Ni * 3) + 1));
			mNormBuffer.put(nlist.get((fplist.get(j).Ni * 3) + 2));
		}

		mIndexBuffer = CharBuffer.allocate(fplist.size());
		for (int j = 0; j < fplist.size(); j++) {
			mIndexBuffer.put((char) j);
		}

		mVertexBuffer.position(0);
		mTexBuffer.position(0);
		mNormBuffer.position(0);
		mIndexBuffer.position(0);
		mIndexCount = fplist.size();
	}

	/**
	 * Draws the model
	 * 
	 * @param gl
	 */
	public void draw(GL10 gl, int texture_id) {

		gl.glPushMatrix();	 
		// Counter-clockwise winding.
		gl.glFrontFace(GL10.GL_CCW);
		//gl.glFrontFace(GL10.GL_CW);
		// Enable face culling.
		if(this.culling){
		  gl.glEnable(GL10.GL_CULL_FACE);
		  // What faces to remove with the face culling.
		  gl.glCullFace(GL10.GL_BACK);
		}
		//gl.glEnable(GL10.GL_NORMALIZE);
		
		// Enabled the vertices buffer for writing and to be used during
		// rendering.
		gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);		
		// Specifies the location and data format of an array of vertex
		// coordinates to use when rendering.
		gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexBuffer);
		
		gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
		gl.glNormalPointer(GL10.GL_FLOAT, 0, mNormBuffer);

		if ((texture_id >=0) && textures != null && textures.hasTexture()){
			gl.glEnable(GL10.GL_TEXTURE_2D);
			// Enable the texture state
			gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
			gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mTexBuffer);
			textures.setTexture(texture_id);
		}
			
	    // Point out the where the color buffer is.
		gl.glDrawElements(GL10.GL_TRIANGLES, mIndexCount, GL10.GL_UNSIGNED_SHORT, mIndexBuffer);
		
		// Disable the vertices buffer.
		gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);
		gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
		
		if (textures != null && textures.hasTexture()){
		  gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
		  gl.glDisable(GL10.GL_TEXTURE_2D);
		}
		
		// Disable face culling.
		gl.glDisable(GL10.GL_CULL_FACE);					

		gl.glPopMatrix();
	}

	private class Fp {
		public long Vi;
		public int Ti;
		public int Ni;

		public Fp(long chi, int ti, int ni) {
			Vi = chi;
			Ti = ti;
			Ni = ni;

		}
	}
	
	public void setCulling(boolean b) {
		culling = b;		
	}

	private GL10 gl;
	private GLTextures textures;
	private int mIndexCount;
	private FloatBuffer mVertexBuffer;
	private FloatBuffer mTexBuffer;
	private FloatBuffer mNormBuffer;
	private CharBuffer mIndexBuffer;
	private Context mContext;
	private boolean culling=true;


}
